package com.zbest.common.lang.util;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Currency;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;

import com.zbest.common.lang.base.AbstractPageableRequest;
import com.zbest.common.lang.base.AbstractResponse;
import com.zbest.common.lang.base.BaseCheckedException;
import com.zbest.common.lang.base.BaseException;
import com.zbest.common.lang.base.Money;
import com.zbest.common.lang.base.Quty;
import com.zbest.common.lang.enums.BizCode;
import com.zbest.common.lang.enums.SystemCode;
import com.zbest.common.lang.exception.BizException;
import com.zbest.common.lang.exception.ValidateException;

/**
 * 通用辅助函数<br/>
 *
 * Created by wu.charles on 2014/12/10.
 */
public abstract class CoreCommonUtils {
    private CoreCommonUtils() {
        ; // nothing.
    }

    /**
     * 判断编码是否为成功<br/>
     *
     * @param code
     * @return
     */
    public static boolean isSuccess(BizCode code) {
        return code != null && code == BizCode.SUCCESS;
    }

    /**
     * 在异常的情况下填充返回值<br/>
     *
     * @param response
     * @param ex
     */
    public static void fillWhenException(AbstractResponse response, Exception ex) {
        if (ex == null) { // support null.
            return;
        }

        if (ex instanceof BaseException) {
            BaseException $ex = (BaseException) ex;
            response.setCode($ex.getCode());
        } else if (ex instanceof BaseCheckedException) {
            BaseCheckedException $ex = (BaseCheckedException) ex;
            response.setCode($ex.getCode());
        } else { // 其他不明确的异常情况
            response.setCode(BizCode.UNKNOWN);
        }

        response.setMessage(ex.getMessage());
    }

    /**
     * 设置返回值成功编码<br/>
     *
     * @param response
     */
    public static void fillSuccess(AbstractResponse response) {
        if (response == null) {
            return;
        }

        response.setCode(BizCode.SUCCESS);
        response.setMessage(BizCode.SUCCESS.desc());
    }

    /**
     * 触发运行时业务异常，抛出的异常类型{@link com.zbest.common.lang.exception.BizException}<br/>
     *
     * @param code
     */
    public static void raiseBizException(BizCode code) {
        raiseBizException(code, code.desc());
    }

    /**
     * 触发运行时业务异常，抛出的异常类型{@link com.zbest.common.lang.exception.BizException}<br/>
     *
     * @param code
     * @param message
     */
    public static void raiseBizException(BizCode code, String message) {
        raiseBizException(code, message, null);
    }

    /**
     * 触发参数验证异常，抛出的异常类型{@link com.zbest.common.lang.exception.ValidateException}<br/>
     *
     * @param code
     * @param message
     */
    public static void raiseValidateException(BizCode code, String message) {
        throw new ValidateException(code, message);
    }

    /**
     * 触发运行时业务异常，抛出的异常类型{@link com.zbest.common.lang.exception.BizException}<br/>
     *
     * @param code
     * @param message
     * @param cause
     */
    public static void raiseBizException(BizCode code, String message, Throwable cause) {
        if (code == null) {
            throw new IllegalArgumentException("抛出业务异常时code不能为null");
        }

        throw new BizException(code, message, cause);
    }

    /**
     * 如果返回值错误，则抛出异常<br/>
     *
     * @param resp
     */
    public static void raiseIfError(AbstractResponse resp) {
        if (resp == null || resp.isSuccess()) {
            return;
        }

        raiseBizException(resp.getCode(), resp.getMessage());
    }

    /**
     * 将check异常转换为运行时异常<br/>
     *
     * @param ex
     */
    public static void raiseRuntimeException(Exception ex) {
        if (ex instanceof RuntimeException) {
            throw (RuntimeException) ex;
        } else {
            raiseBizException(BizCode.UNKNOWN, "非运行时异常!", ex);
        }
    }

    /**
     * 复制接口返回值的公共数据<br/>
     *
     * @param from
     * @param to
     */
    public static void copy(AbstractResponse from, AbstractResponse to) {
        if (from == null || to == null) {
            return;
        }

        to.setMessage(from.getMessage());
        to.setCode(from.getCode());
        to.setClientHostName(from.getClientHostName());
        to.setClientIp(from.getClientIp());
        to.setServerHostName(from.getServerHostName());
        to.setServerIp(from.getServerIp());
        to.copy(from.extFields());
    }

    /**
     * 计算收益<br/>
     *
     * @param nav
     * @param quty
     * @param currency
     * @return
     */
    public static Money calcYield(BigDecimal nav, Quty quty, Currency currency) {
        BigDecimal total = nav.multiply(quty.toFloat());

        return new Money(total, currency);
    }

    /**
     * 判断字符串是否为数字<br>
     * 
     * @param src
     * @return
     */
    public static boolean isNumeric(String src) {
        Pattern pattern = Pattern.compile("[0-9]*");
        return pattern.matcher(src).matches();
    }

    /**
     * 比较两个BigDecimal的大小，不能为null<br/>
     *
     * @param left
     * @param right
     * @return
     */
    public static int compareTo(BigDecimal left, BigDecimal right) {
        return left.compareTo(right);
    }

    /**
     * 判断给定的值是否大于0<br/>
     *
     * @param value
     * @return
     */
    public static boolean greater0(BigDecimal value) {
        return compareTo(BigDecimal.ZERO, value) < 0;
    }

    /**
     * 判断给定的值是否大于等于0<br/>
     *
     * @param value
     * @return
     */
    public static boolean greaterOrEqual0(BigDecimal value) {
        return compareTo(BigDecimal.ZERO, value) <= 0;
    }

    /**
     * 判断给定的值是否等于0<br/>
     *
     * @param value
     * @return
     */
    public static boolean is0(BigDecimal value) {
        return value != null && BigDecimal.ZERO.equals(value);
    }

    /**
     * 判断给定的值是否等于0或者为null<br/>
     *
     * @param value
     * @return
     */
    public static boolean isnullOr0(BigDecimal value) {
        return value == null || BigDecimal.ZERO.equals(value);
    }

    /**
     * 将null转换为0的值<br/>
     *
     * @param value
     * @return
     */
    public static BigDecimal nullTo0(BigDecimal value) {
        return value == null ? BigDecimal.ZERO : value;
    }

    /**
     * 将0转换为null的值<br/>
     *
     * @param value
     * @return
     */
    public static BigDecimal zeroToNull(BigDecimal value) {
        return value == null || is0(value) ? null : value;
    }

    /**
     * 根据货币的cent和货币类型，构造份额实例<br/>
     *
     * @param value
     * @param scale
     * @return
     */
    public static Quty fromValue(String value, String scale) {
        if (StringUtils.isEmpty(value)) {
            return null;
        }

        return new Quty(Long.parseLong(value), Integer.parseInt(scale));
    }

    /**
     * 根据货币的cent和货币类型，构造份额实例<br/>
     *
     * @param value
     * @param scale
     * @return
     */
    public static Quty fromValue(BigInteger value, String scale) {
        if (value == null) {
            return null;
        }

        return new Quty(value.longValue(), Integer.parseInt(scale));
    }

    /**
     * 根据货币的cent和货币类型，构造份额实例<br/>
     *
     * @param value
     * @param scale
     * @return
     */
    public static Quty fromValue(BigInteger value, Integer scale) {
        if (value == null) {
            return null;
        }

        return new Quty(value.longValue(), scale);
    }

    /**
     * 获取份额的整数值<br/>
     *
     * @param quty
     * @return
     */
    public static BigInteger qutyValue(Quty quty) {
        if (quty == null) {
            return null;
        }

        return BigInteger.valueOf(quty.getValue());
    }

    /**
     * 获取份额的整数值<br/>
     *
     * @param quty
     * @return
     */
    public static Long qutyValueL(Quty quty) {
        if (quty == null) {
            return null;
        }

        return quty.getValue();
    }

    /**
     * 根据份额值和小数位数，构造货币实例<br/>
     *
     * @param cent
     * @param currencyCode
     * @return
     */
    public static Money fromCent(String cent, String currencyCode) {
        if (StringUtils.isEmpty(cent)) {
            return null;
        }

        return new Money(Long.parseLong(cent), Currency.getInstance(currencyCode));
    }

    /**
     * 根据份额值和小数位数，构造货币实例<br/>
     *
     * @param cent
     * @param currencyCode
     * @return
     */
    public static Money fromCent(BigInteger cent, String currencyCode) {
        if (cent == null) {
            return null;
        }

        return new Money(cent.longValue(), Currency.getInstance(currencyCode));
    }

    /**
     * 获取金额的整数值<br/>
     *
     * @param money
     * @return
     */
    public static BigInteger moneyValue(Money money) {
        if (money == null) {
            return null;
        }

        return BigInteger.valueOf(money.getCent());
    }

    /**
     * 获取金额的整数值<br/>
     *
     * @param money
     * @return
     */
    public static Long moneyValueL(Money money) {
        if (money == null) {
            return null;
        }

        return money.getCent();
    }

    /**
     * 获取当前线程上下文的UUID值<br/>
     *
     * @return
     */
    public static String currThreadUuid() {
        try {
            return (get_uuid == null) ? null : (String) get_uuid.invoke(null);
        } catch (Exception e) {
            ; // ignore
        }

        return null;
    }

    /**
     * 获取当前线程上下文的Second UUID值<br/>
     *
     * @return
     */
    public static String currThreadUuidb() {
        try {
            return (get_uuidb == null) ? null : (String) get_uuidb.invoke(null);
        } catch (Exception e) {
            ; // ignore
        }

        return null;
    }

    /**
     * 获取当前线程上下文的Application Name值<br/>
     *
     * @return
     */
    public static String currThreadAppName() {
        try {
            return (get_applicationName == null) ? null : (String) get_applicationName.invoke(null);
        } catch (Exception e) {
            ; // ignore
        }

        return null;
    }

    /**
     * 设置digist msg<br/>
     *
     * @return
     */
    public static void setDigistMsg(String msg) {
        try {
            if (set_digistMsg != null) {
                set_digistMsg.invoke(null, msg);
            }
        } catch (Exception e) {
            ; // ignore
        }
    }

    /**
     * 获取最合适的class loader实例<br/>
     *
     * @param clazz
     * @return
     */
    public static ClassLoader getClassLoader(Class<?> clazz) {
        if (clazz == null) {
            return ClassLoader.getSystemClassLoader();
        }

        final ClassLoader loader = Thread.currentThread().getContextClassLoader();
        if (loader != null) {
            return loader;
        }

        return clazz.getClassLoader();
    }

    /**
     * mysql数据库分页的起始索引值<br/>
     * 主要用于: limit  start, pageSize<br/>
     *
     * @param request
     * @return
     */
    public static int mysqlPageStart(AbstractPageableRequest request) {
        return (request.getPageNo() - 1) * request.getItemPerPage();
    }

    /**
     * 实例化BizCode<br/>
     *
     * @param name
     * @param serviceCode
     * @param code
     * @param desc
     * @return
     */
    public static BizCode newBizCode(String name, SystemCode serviceCode, String code, String desc) {
        if (serviceCode == null || StringUtils.isEmpty(code) || StringUtils.isEmpty(name)) {
            return null;
        }

        BizCode bizCode = fetch(name, serviceCode, code);
        if (bizCode != null) {
            return bizCode;
        }

        synchronized (BizCode.class) {
            // double check!!
            bizCode = fetch(name, serviceCode, code);
            if (bizCode != null) {
                return bizCode;
            }

            return new BizCode(name, serviceCode, code, desc);
        }
    }

    private static BizCode fetch(String name, SystemCode serviceCode, String code) {
        BizCode bizCode = BizCode.valueByCode(BizCode.class, serviceCode.code() + code);
        if (bizCode != null) {
            return bizCode;
        }

        bizCode = BizCode.valueOf(BizCode.class, serviceCode.code() + name);
        if (bizCode != null) {
            return bizCode;
        }

        return null;
    }

    // --------------------------------------- private methods
    private static Method get_uuid            = null;
    private static Method get_uuidb           = null;
    private static Method set_digistMsg       = null;
    private static Method get_applicationName = null;

}
